/*
   Copyright (c) 2009-2009 LW, Inc. <http://www.lw.com>
   This file is part of LWFS.

   LWFS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published
   by the Free Software Foundation; either version 3 of the License,
   or (at your option) any later version.

   LWFS is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see
   <http://www.gnu.org/licenses/>.
*/


#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include "lwfs.h"
#include "xlator.h"
#include "combine.h"
#include "defaults.h"

#include <sys/time.h>
#include <libgen.h>

int
combine_init_subvolumes (xlator_t *this, combine_conf_t *conf)
{
        xlator_list_t *subvols = NULL;
        int            cnt = 0;


        for (subvols = this->children; subvols; subvols = subvols->next)
                cnt++;

        conf->subvolumes = CALLOC (cnt, sizeof (xlator_t *));
        if (!conf->subvolumes) {
                gf_log (this->name, GF_LOG_ERROR,
                        "Out of memory");
                return -1;
        }
        conf->subvolume_cnt = cnt;

        cnt = 0;
        for (subvols = this->children; subvols; subvols = subvols->next)
                conf->subvolumes[cnt++] = subvols->xlator;

	conf->subvolume_status = CALLOC (cnt, sizeof (char));
	if (!conf->subvolume_status) {
		gf_log (this->name, GF_LOG_ERROR,
			"Out of memory");
		return -1;
	}

        return 0;
}
int
combine_frame_return (call_frame_t *frame)
{
	combine_local_t *local = NULL;
	int          this_call_cnt = -1;

	if (!frame)
		return -1;

	local = frame->local;

	LOCK (&frame->lock);
	{
		this_call_cnt = --local->call_cnt;
	}
	UNLOCK (&frame->lock);

	return this_call_cnt;
}

void
combine_local_wipe (xlator_t *this, combine_local_t *local)
{
	if (!local)
		return;

	loc_wipe (&local->loc);

	if (local->inode)
		inode_unref (local->inode);

	if (local->fd) {
		fd_unref (local->fd);
		local->fd = NULL;
	}

	FREE (local);
}


combine_local_t *
combine_local_init (call_frame_t *frame)
{
	combine_local_t *local = NULL;

	/* TODO: use mem-pool */
	local = CALLOC (1, sizeof (*local));

	if (!local)
		return NULL;

	local->op_ret = -1;
	local->op_errno = EUCLEAN;

	frame->local = local;

	return local;
}

int
combine_subvol_cnt (xlator_t *this, xlator_t *subvol)
{
	int i = 0;
	int ret = -1;
	combine_conf_t *conf = NULL;


	conf = this->private;

	for (i = 0; i < conf->subvolume_cnt; i++) {
		if (subvol == conf->subvolumes[i]) {
			ret = i;
			break;
		}
	}

	return ret;
}

int
combine_itransform (xlator_t *this, xlator_t *subvol, uint64_t x, uint64_t *y_p)
{
	combine_conf_t *conf = NULL;
	int         cnt = 0;
	int         max = 0;
	uint64_t    y = 0;


	if (x == ((uint64_t) -1)) {
		y = (uint64_t) -1;
		goto out;
	}

	conf = this->private;

	max = conf->subvolume_cnt;
	cnt = combine_subvol_cnt (this, subvol);

	y = ((x * max) + cnt);

out:
	if (y_p)
		*y_p = y;

	return 0;
}
int
combine_deitransform (xlator_t *this, uint64_t y, xlator_t **subvol_p,
		uint64_t *x_p)
{
	combine_conf_t *conf = NULL;
	int         cnt = 0;
	int         max = 0;
	uint64_t    x = 0;
	xlator_t   *subvol = 0;


	conf = this->private;
	max = conf->subvolume_cnt;

	cnt = y % max;
	x   = y / max;

	subvol = conf->subvolumes[cnt];

	if (subvol_p)
		*subvol_p = subvol;

	if (x_p)
		*x_p = x;

	return 0;
}

combine_stat_merge (xlator_t *this, struct stat *to,
		struct stat *from, xlator_t *subvol)
{
        if (!from || !to)
                return 0;

	to->st_dev      = from->st_dev;

	combine_itransform (this, subvol, from->st_ino, &to->st_ino);

	to->st_mode     = from->st_mode;
	to->st_nlink    = from->st_nlink;
	to->st_rdev     = from->st_rdev;
	to->st_size    += from->st_size;
	to->st_blksize  = from->st_blksize;
	to->st_blocks  += from->st_blocks;

	set_if_greater (to->st_uid, from->st_uid);
	set_if_greater (to->st_gid, from->st_gid);

	set_if_greater (to->st_atime, from->st_atime);
	set_if_greater (to->st_mtime, from->st_mtime);
	set_if_greater (to->st_ctime, from->st_ctime);

	return 0;
}
int32_t
combine_fd_set_ctx (fd_t *file, xlator_t *this, loc_t *loc, combine_fd_ctx_t *ctx)
{
        uint64_t oldaddr = 0;
        int32_t  ret = -1;

        GF_VALIDATE_OR_GOTO ("combine", this, out);
        GF_VALIDATE_OR_GOTO (this->name, file, out);

        ret = fd_ctx_get (file, this, &oldaddr);
        if (ret >= 0) {
                gf_log (this->name, GF_LOG_DEBUG,
                        "%s (%"PRId64"): trying duplicate remote fd set. ",
                        loc->path, loc->inode->ino);
        }

        ret = fd_ctx_set (file, this, (uint64_t)(unsigned long)ctx);
        if (ret < 0) {
                gf_log (this->name, GF_LOG_DEBUG,
                        "%s (%"PRId64"): failed to set remote fd",
                        loc->path, loc->inode->ino);
		goto out;
        }
	ret = 0;
out:
        return ret;
}
xlator_t
*combine_vol_by_loc (xlator_t *this, loc_t *loc)
{
	combine_conf_t *conf = NULL;
	char *volname = NULL;
	int i = 0;

	GF_VALIDATE_OR_GOTO ("combine", this, err);
	GF_VALIDATE_OR_GOTO (this->name, loc->path, err);

	conf = this->private;
	volname = strtok(loc->path, "/");
	if (volname == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Get vol name from path %s failed",
			loc->path);
		goto err;
	}
	for (i = 0; i < conf->subvolume_cnt; i++) {
		if (!strcmp(conf->subvolumes[i]->name, volname))
			return conf->subvolumes[i];
	} 
err:
	return NULL;
}

xlator_t *
combine_subvol_next (xlator_t *this, xlator_t *prev)
{
	combine_conf_t *conf = NULL;
	int         i = 0;
	xlator_t   *next = NULL;

	conf = this->private;

	for (i = 0; i < conf->subvolume_cnt; i++) {
		if (conf->subvolumes[i] == prev) {
			if ((i + 1) < conf->subvolume_cnt)
				next = conf->subvolumes[i + 1];
			break;
		}
	}

	return next;
}

int
combine_is_local_loc (xlator_t *this, loc_t *loc)
{
	int slash_count = 1;
	char *path = NULL, *tk = NULL;

	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (loc, err);
	VALIDATE_OR_GOTO (loc->inode, err);
	VALIDATE_OR_GOTO (loc->path, err);

	path = strdup (loc->path);
	if (path == NULL)
		goto err;

	tk = strtok (path, "/");
	while (strtok (NULL, "/"))
		slash_count ++;

	if (slash_count > 2)
		return 0;
	else
		return 1;
err:
	if (path != NULL)
		free (path);
	return 1;
}
